﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using xunsearch.lib;
namespace xunsearch.lib
{

    public class XSFieldScheme
    {
        public const int MIXED_VNO = 255;
        private Dictionary<string,XSFieldMeta> _fields=new Dictionary<string,XSFieldMeta>();
        private Dictionary<int, string> _typeMap=new Dictionary<int,string>();
        private Dictionary<string, string> _vnoMap=new Dictionary<string,string>();

        private static object _logger;

        /// <summary>
        /// 将对象转换为配置文件字符串
        /// </summary>
        /// <returns></returns>
        public string __toString() 
        {
            string str = "";
            foreach (var field in _fields) 
            {
                str += field.Value.toConfig() + "\n";
            }
            return str;
        }

        /// <summary>
        /// 获取主键字段元数据
        /// </summary>
        /// <returns>XSFieldMeta 类型为 ID 的字段</returns>
        public XSFieldMeta getFieldId() 
        {
            if (_typeMap.ContainsKey(XSFieldMeta.TYPE_ID)) 
            {
                string name = _typeMap[XSFieldMeta.TYPE_ID];
                return _fields[name];
            }
            return null;
        }

        /// <summary>
        /// 获取标题字段元数据
        /// </summary>
        /// <returns>XSFieldMeta 类型为 TITLE 的字段</returns>
        public XSFieldMeta getFieldTitle() 
        {
            if(_typeMap.ContainsKey(XSFieldMeta.TYPE_TITLE))
            {
                string name = _typeMap[XSFieldMeta.TYPE_TITLE];
                return _fields[name];
            }
            foreach (var de in _fields) 
            {
                if (de.Value.type == XSFieldMeta.TYPE_STRNG && !de.Value.isBoolIndex()) 
                {
                    return de.Value;
                }
            }
            return null;
        }

        /// <summary>
        /// 获取内容字段元数据
        /// </summary>
        /// <returns>XSFieldMeta 类型为 BODY 的字段</returns>
        public XSFieldMeta getFieldBody() 
        {
            if (_typeMap.ContainsKey(XSFieldMeta.TYPE_BODY)) 
            {
                return _fields[_typeMap[XSFieldMeta.TYPE_BODY]];
            }
            return null;
        }

        /// <summary>
        /// 获取项目字段元数据
        /// </summary>
        /// <param name="name">字段名称(string) 或字段序号(vno, int)</param>
        /// <param name="is_throw">当字段不存在时是否抛出异常, 默认为 true</param>
        /// <returns>字段元数据对象, 若不存在则返回 null</returns>
        public XSFieldMeta getField(string name,bool is_throw=true)
        {
            if (IsNumberic(name)) 
            {
                if (!_vnoMap.ContainsKey(name)) 
                {
                    if (is_throw) 
                    {
                        throw new Exception("Not exists field with vno: '" + name + "'");
                    }
                    return null;
                }
                name = _vnoMap[name];
            }
            if (!_fields.ContainsKey(name)) 
            {
                if (is_throw) 
                {
                    throw new Exception("Not exists field with vno: '" + name + "'");
                }
                return null;
            }
            return _fields[name];
        }

        /// <summary>
        /// 获取项目所有字段结构设置
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, XSFieldMeta> getAllFields() 
        {
            return _fields;
        }

        /// <summary>
        /// 获取所有字段的vno与名称映映射关系
        /// </summary>
        /// <returns>vno为键, 字段名为值的数组</returns>
        public Dictionary<string, string> getVnoMap() 
        {
            return _vnoMap;
        }

        public void addField(string field, Dictionary<string, string> config = null) 
        {
            XSFieldMeta obj = new XSFieldMeta(field, config);
            addField(obj);
        }
        public void addField(XSFieldMeta field, Dictionary<string, string> config = null) 
        {
            if(_fields.ContainsKey(field.name))
            {
                throw new Exception("Duplicated field name: '" + field.name + "'");
            }
            if (field.isSpeical()) 
            {
                if (_typeMap.ContainsKey(field.type)) 
                {
                    string prev = _typeMap[field.type];
                    throw new Exception("Duplicated " + config["type"] + " field: `" + field.name + "' and `" + prev + "'");
                }
                _typeMap.Add(field.type, field.name);
            }

            field.vno = (field.type == XSFieldMeta.TYPE_BODY) ? MIXED_VNO : _vnoMap.Count;
            if (!_vnoMap.ContainsKey(field.vno.ToString())) 
            {
                _vnoMap.Add(field.vno.ToString(), field.name);
            }

            if (field.type == XSFieldMeta.TYPE_ID)
            {
                _fields.Add(field.name, field);
            }
            else 
            {
                if (_fields.ContainsKey(field.name))
                {
                    _fields[field.name] = field;
                }
                else 
                {
                    _fields.Add(field.name, field);
                }
            }
        }

        /// <summary>
        /// 判断该字段方案是否有效、可用
        /// 每个方案必须并且只能包含一个类型为 ID 的字段
        /// </summary>
        /// <param name="is_throw"></param>
        /// <returns></returns>
        public bool checkValid(bool is_throw = false) 
        {
            if (!_typeMap.ContainsKey(XSFieldMeta.TYPE_ID)) 
            {
                if (is_throw) 
                {
                    throw new Exception("Missing field of type ID");
                }
                return false;
            }
            return true;
        }

        public Dictionary<string, XSFieldMeta> getFields() 
        {
            return _fields;
        }

        /// <summary>
        /// 获取搜索日志的字段方案
        /// </summary>
        /// <returns></returns>
        public static object logger()
        {
            if (_logger == null) 
            {
                XSFieldScheme scheme = new XSFieldScheme();
                Dictionary<string,string> di=new Dictionary<string,string>();
                di.Add("type","id");
                scheme.addField("id", di);
                scheme.addField("pinyin");
                scheme.addField("partial");
                di.Clear();
                di.Add("type","numeric");
                di.Add("index","self");
                scheme.addField("total", di);
                scheme.addField("lastnum", di);
                scheme.addField("currnum", di);
                di.Clear();
                di.Add("type", "string");
                scheme.addField("currtag", di);
                di["type"] = "body";
                scheme.addField("body", di);
                _logger = scheme;
            }
            return _logger;
        }
        private bool IsNumberic(string oText)
        {
            try
            {
                Decimal Number = Convert.ToDecimal(oText);
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
    public class XSFieldMeta
    {
        //词条权重最大值
        const int MAX_WDF = 0x3f;

        //字段类型常量定义
        public const int TYPE_STRNG = 0;
        public const int TYPE_NUMERIC = 1;
        public const int TYPE_DATE = 2;
        public const int TYPE_ID = 10;
        public const int TYPE_TITLE = 11;
        public const int TYPE_BODY = 12;

        //索引标志常量定义
        const int FLAG_INDEX_SELF = 0x01;
        const int FLAG_INDEX_MIXED = 0x02;
        const int FLAG_INDEX_BOTH = 0x03;
        const int FLAG_WITH_POSITION = 0x10;
        const int FLAG_NON_BOOL = 0x80;


        public string name;
        public int cutlen = 0;

        /// <summary>
        /// 混合区检索时的相对权重
        /// </summary>
        public int weight = 1;

        /// <summary>
        /// 字段类型
        /// </summary>
        public int type = 0;

        /// <summary>
        /// 字段序号
        /// </summary>
        public int vno = 0;

        /// <summary>
        /// 词法分析器
        /// </summary>
        private string tokenizer = "";

        private int flag = 0;

        private static Dictionary<string, object> _tokenizers = new Dictionary<string, object>();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name"></param>
        /// <param name="config"></param>
        public XSFieldMeta(string name, Dictionary<string,string> config) 
        {
            this.name = name;
            if (config != null && config.Count > 0) 
            {
                fromConfig(config);
            }
           
        }
        /// <summary>
        /// 将对象转换为字符串
        /// </summary>
        /// <returns></returns>
        public string __toString() 
        {
            return name;
        }

        /// <summary>
        /// 把给定的值转换为符合这个字段的数据格式
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string val(string value) 
        {
            if (type == TYPE_DATE) {
                //日期类型: 转换成专用的 YYYYmmdd 格式
                value = DateTime.Parse(value).ToString("yyyyMMdd").ToString();
            }
            return value;
        }

        /// <summary>
        /// 判断当前字段索引是否支持短语搜索
        /// </summary>
        /// <returns>是返回 true, 不是返回 false</returns>
        public bool withPos() 
        {
            return (flag & FLAG_WITH_POSITION)>0 ? true : false;
        }

        public bool isBoolIndex() 
        {
            if ((flag & FLAG_NON_BOOL)>0)
            {
                return false;
            }
            return (!hasIndex() || tokenizer != "");
        }

        /// <summary>
        /// 判断当前字段是否为数字型
        /// </summary>
        /// <returns></returns>
        public bool isNumeric() 
        {
            return (type == TYPE_NUMERIC);
        }

        /// <summary>
        /// 判断当前字段是否为特殊类型
        /// </summary>
        /// <returns></returns>
        public bool isSpeical() 
        {
            return (type == TYPE_ID || type == TYPE_TITLE || type == TYPE_BODY);
        }
        /// <summary>
        /// 判断当前字段是否需要索引
        /// </summary>
        /// <returns></returns>
        public bool hasIndex() 
        {
            return (flag & FLAG_INDEX_BOTH) > 0 ? true : false;
        }
        /// <summary>
        /// 判断当前字段是否需要在混合区索引
        /// </summary>
        /// <returns></returns>
        public bool hasIndexMixed() 
        {
            return (flag & FLAG_INDEX_MIXED) > 0 ? true : false;
        }

        /// <summary>
        /// 判断当前字段是否需要在字段区索引
        /// </summary>
        /// <returns></returns>
        public bool hasIndexSelf() 
        {
            return (flag & FLAG_INDEX_SELF) > 0 ? true : false;
        }
        /// <summary>
        /// 判断当前字段是否采用自定义分词器
        /// </summary>
        /// <returns></returns>
        public bool hasCustomTokenizer() 
        {
            return (tokenizer != "");//todo
        }

        /// <summary>
        /// 获取自定义词法分析器
        /// </summary>
        /// <returns></returns>
        public object getCustomTokenizer() 
        {
            string arg = "";
            string typename = "";
            if (_tokenizers.ContainsKey(tokenizer))
            {
                return _tokenizers[tokenizer];
            }
            else 
            {
                int pos1=0;int pos2=0;
                if ((pos1 = tokenizer.IndexOf("(")) > 0 && ((pos2 = tokenizer.IndexOf(")", pos1 + 1)) > 0))
                {
                    typename = "xunsearch.lib.XSTokenizer" + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(tokenizer.Substring(0, pos1));
                    arg = tokenizer.Substring(pos1 + 1, pos2 - pos1 - 1);
                }
                else 
                {
                    typename = "xunsearch.lib.XSTokenizer" + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(tokenizer);
                }
                try
                {
                    Type t = Type.GetType(typename);
                    object obj = System.Activator.CreateInstance(t);
                    _tokenizers.Add(tokenizer, obj);
                    return obj;
                }
                catch (Exception ex) 
                {
                    HttpContext.Current.Response.Write(ex.Message);
                    HttpContext.Current.Response.End();
                }
                return null;
            }
        }

        public string toConfig() 
        {
            //type
            string str = "[" + name + "]\n";
            switch (type) 
            {
                case TYPE_NUMERIC:
                    str += "type = numeric\n";
                    break;
                case TYPE_DATE:
                    str += "type = date\n";
                    break;
                case TYPE_ID:
                    str += "type = id\n";
                    break;
                case TYPE_TITLE:
                    str += "type = title\n";
                    break;
                case TYPE_BODY:
                    str += "type = body\n";
                    break;
            }
           
            //index
            int index=0;
            if (type != TYPE_BODY && (index = (flag & FLAG_INDEX_BOTH)) > 0) 
            {
                if (index == FLAG_INDEX_BOTH) 
                {
                    if (type == TYPE_TITLE) 
                    {
                        str += "index = both\n";
                    }
                }
                else if (index == FLAG_INDEX_MIXED)
                {
                    str += "index = mixed\n";
                }
                else 
                {
                    if (type != TYPE_ID)
                    {
                        str += "index = self\n";
                    }
                }
            }

            //tokenizer
            if (type != TYPE_ID && tokenizer != "0") //todo
            {
                str += "tokenizer =" + tokenizer + "\n";
            }

            //cutlen
            if (cutlen > 0 && !(cutlen == 300 && type == TYPE_BODY)) 
            {
                str += "cutlen =" + cutlen.ToString() + "\n";
            }

            //weight
            if (weight != 1 && !(weight == 5 && type == TYPE_TITLE)) 
            {
                str += "weight = " + weight.ToString() + "\n";
            }

            //phrase
            if ((flag & FLAG_WITH_POSITION) > 0)
            {
                if (type == TYPE_BODY && type != TYPE_TITLE)
                {
                    str += "phrase = yes\n";
                }
            }
            else 
            {
                str += "phrase = no\n";
            }

            // non-bool
            if ((flag & FLAG_NON_BOOL) > 0) 
            {
                str += "non_bool = yes\n";
            }
            return str;
        }
        /// <summary>
        /// 解析字段对象属性
        /// </summary>
        /// <param name="config"></param>
        public void fromConfig(Dictionary<string, string> config) 
        {
            if(config.ContainsKey("type")){
                string typename = "TYPE_" + config["type"].ToUpper();
                type = (int)this.GetType().GetField(typename).GetValue(this);
                if (type == TYPE_ID) 
                {
                    flag = FLAG_INDEX_SELF;
                    tokenizer = "full";
                }
                else if (type == TYPE_TITLE)
                {
                    flag = FLAG_INDEX_BOTH | FLAG_WITH_POSITION;
                    weight = 5;
                }
                else if (type == TYPE_BODY) 
                {
                    vno = 0;//todo
                    flag = FLAG_INDEX_SELF | FLAG_WITH_POSITION;
                    cutlen = 300;
                }
            }

            //index  flag
            if (config.ContainsKey("index") && type == TYPE_BODY)
            {
                string predef = config["index"].ToLower();
                if (predef == "self" || predef=="mixed" || predef=="both") {
                    flag &= ~FLAG_INDEX_BOTH;
                    flag |= (predef == "self") ? FLAG_INDEX_SELF : ((predef == "mixed") ? FLAG_INDEX_MIXED : FLAG_INDEX_BOTH);
                }
                if (type == TYPE_ID)
                {
                    flag |= FLAG_INDEX_SELF;
                }
            }

            //others
            if (config.ContainsKey("cutlen")) 
            {
                cutlen = int.Parse(config["cutlen"]);
            }

            if (config.ContainsKey("weight") && type != TYPE_BODY) 
            {
                weight = int.Parse(config["weight"]) & MAX_WDF;
            }

            if (config.ContainsKey("phrase")) 
            {
                if (config["phrase"].ToLower() == "yes") 
                {
                    flag |= FLAG_WITH_POSITION;
                }
                else if (config["phrase"].ToLower() == "no")
                {
                    flag &= ~FLAG_WITH_POSITION;
                }
            }

            if (config.ContainsKey("non_bool")) 
            {
                if(config["non_bool"].ToLower()=="yes")
                {
                    flag |= FLAG_NON_BOOL;
                }
                else if (config["non_bool"].ToLower() == "no") 
                {
                    flag &= ~FLAG_NON_BOOL;
                }
            }

            if (config.ContainsKey("tokenizer") && type != TYPE_ID && config["tokenizer"] == "default")
            {
                tokenizer = config["tokenizer"];
            }
        }
    }
}
